# -*- coding: utf-8 -*-
#
print("当前版本：V1.1.4")

import json
import random
import re
import threading
import time
from datetime import datetime

import pymysql
import pytz
import requests
from airscript.intent import Intent
# noinspection PyUnresolvedReferences
from android.net import Uri
# noinspection PyUnresolvedReferences
from android.provider import Settings
# noinspection PyUnresolvedReferences
from android.view.animation import DecelerateInterpolator
from ascript.android import action, system
from ascript.android import plug
from ascript.android.node import Selector
from ascript.android.system import Device
from ascript.android.system import KeyValue
from ascript.android.system import R

plug.load("deepseek")

display = Device.display()
s_w = display.widthPixels
s_h = display.heightPixels

# URL = 'https://gitee.com/liang_shanshui/dyuser/raw/master'
# config_url = f'{URL}/res/config.json'
# talk_url = f'{URL}/res/talk.json'
# face_url = f'{URL}/res/face.json'
# say_url = f'{URL}/res/say.json'
db_config = {
    'host': '8.140.162.237',
    'port': 3307,
    'user': 'bcd986',
    'password': 'lxf@7788',
    'database': 'db_dev_17005',
    'charset': 'utf8mb4',
    'autocommit': True
}

AIKEY = "sk-17139b19e50f488da9b215bcb4a28c58"
weixin = '123456789'


def get_one(m, n=0, timeout=3, interval=1):
    """
    根据 n 选择查找方式：
    n=0: id查找
    n=1: text查找
    n=2: desc查找
    n=3: packageName查找

    m: 查找内容字符串
    timeout: 最大等待时间，单位秒
    interval: 重试间隔秒数

    返回找到的控件对象，找不到返回 None
    """
    start_time = time.time()
    while True:
        try:
            if n == 0:
                oo = Selector().id(m).find_all()
            elif n == 1:
                oo = Selector().text(m).find_all()
            elif n == 2:
                oo = Selector().desc(m).find_all()
            elif n == 3:
                oo = Selector().packageName(m).find_all()
            else:
                print(f"未知查找类型 n={n}")
                return None

            for o in oo:
                if o and rect_in_screen(o.rect):
                    return o

            if time.time() - start_time > timeout:
                # print(f"查找控件超时，类型={n}，内容='{m}'")
                return None

            time.sleep(interval)
        except Exception as e:
            # print(f"查找控件异常: {e}")
            if time.time() - start_time > timeout:
                return None
            time.sleep(interval)


def random_text(lst, min_num=1, max_num=7, allow_repeat=True):
    """
    从列表 lst 中随机抽取元素，数量在 min_num 到 max_num 之间。
    可以选择是否允许重复抽取。

    参数:
        lst (list): 源列表
        min_num (int): 最少抽取元素数量，至少为1
        max_num (int): 最多抽取元素数量，不小于 min_num
        allow_repeat (bool): 是否允许重复抽取，默认允许

    返回:
        list: 抽取的元素列表
    """
    if not lst:
        return []

    min_num = max(1, min_num)
    max_num = max(min_num, max_num)

    count = random.randint(min_num, max_num)

    if allow_repeat:
        # 允许重复，使用 random.choices
        return random.choices(lst, k=count)
    else:
        # 不允许重复，使用 random.sample，数量不能超过列表长度
        count = min(count, len(lst))
        return random.sample(lst, count)


def get_json(url):
    r = requests.get(url)
    if r.status_code == 200:
        return r.json()  # 直接转换成字典
    else:
        print(f"请求失败，状态码：{r.status_code}")
        return None


def rect_center(rect):
    return (rect.left + rect.right) // 2, (rect.top + rect.bottom) // 2


def keyword_in_text(text, keywords):
    """
    判断字符串 text 是否包含 keywords 列表中的任意一个关键字。

    参数：
    - text: 目标字符串
    - keywords: 关键字列表

    返回：
    - True: 包含任意关键字
    - False: 不包含任何关键字
    """
    if not text or not keywords:
        return False
    return any(key in text for key in keywords)


# 正则提取数字
def get_number(str):
    """
    从点赞字符串中提取数字，支持带单位“万”、“千”，返回整数点赞数。
    例如：
    '喜欢3.8万' -> 38000
    '点赞500' -> 500
    '点赞1千' -> 1000
    """
    if not str:
        return 0
    # 先匹配数字部分（支持小数）
    match = re.search(r'(\d+(\.\d+)?)', str)
    if not match:
        return 0
    number = float(match.group(1))
    # 判断单位
    if '万' in str:
        number *= 10000
    return int(number)


# 判断控件在不在屏幕内
def rect_in_screen(rect, screen_width=s_w, screen_height=s_h):
    # 解析坐标
    if isinstance(rect, str):
        nums = re.findall(r'-?\d+', rect)
        if len(nums) != 4:
            raise ValueError(f"无法解析坐标: {rect}")
        left, top, right, bottom = map(int, nums)
    elif hasattr(rect, 'left') and hasattr(rect, 'top') and hasattr(rect, 'right') and hasattr(rect, 'bottom'):
        left, top, right, bottom = rect.left, rect.top, rect.right, rect.bottom
    else:
        left, top, right, bottom = rect
    # 判断是否有效且在屏幕内
    if left < 0 or top < 0 or right < 0 or bottom < 0:
        return False
    if left >= right or top >= bottom:
        return False
    if left >= screen_width or right > screen_width:
        return False
    if top >= screen_height or bottom > screen_height:
        return False
    return True


def parent_to_child(parents, childs):
    if not parents:
        return None
    try:
        return parents.find(childs)
    except Exception as e:
        # print(f"parent_to_child 报错: {e}")
        return None


def swipe_up(start_x=None, start_y=None, end_x=None, end_y=None, duration=300):
    global s_w, s_h  # 确保s_w, s_h是全局变量或提前定义
    if start_x is None or start_y is None or end_x is None or end_y is None:
        start_x, start_y = s_w // 2, int(s_h * 0.7)
        end_x, end_y = s_w // 2, int(s_h * 0.1)
    print("向上滑动")
    interpolator = DecelerateInterpolator()
    action.slide(start_x, start_y, end_x, end_y, duration, interpolator, control_points=0)
    time.sleep(1)


class User_Fans_Page:
    def __init__(self):
        self.粉丝列表页用户昵称ID = 'v6i'
        self.粉丝列表呢称ID = '02='
        self.粉丝ID = '1k8'
        self.粉丝列表头像ID = 'jfn'
        self.粉丝列表框架ID = 'viewpager'
        self.粉丝列表ID = 'root_layout'


class User_Video_Page:
    def __init__(self):
        self.首页菜单ID = "qni"
        self.视频框架ID = "sx9"
        self.短视频页搜索ID = "mmi"
        self.评论框ID = "c9_"
        self.评论框表情ID = "k-a"
        self.评论框发送TEXT = "发送"
        self.昵称ID = "title"
        self.标题ID = "ep+"
        self.关注ID = "h0p"
        self.点赞ID = "ezj"
        self.评论ID = "c_o"
        self.收藏ID = "c6m"
        self.分享ID = "vmj"
        self.查看详情TEXT = ".*查看详情.*"
        self.推荐给朋友ID = "vgn"
        self.推荐给朋友TEXT = "推荐给朋友"

        self.视频框架obj = None
        self.短视频页搜索obj = None
        self.评论框obj = None
        self.评论框发送obj = None
        self.昵称obj = None
        self.标题obj = None
        self.关注obj = None
        self.点赞obj = None
        self.评论obj = None
        self.收藏obj = None
        self.分享obj = None
        self.查看详情obj = None
        self.推荐给朋友obj = None

    def get_data(self):
        视频框架列表 = Selector().id(self.视频框架ID).find_all()
        if not 视频框架列表:
            print("未找到视频框架")
            return None
        for item in 视频框架列表:
            if rect_in_screen(item.rect):
                视频框架 = item
                break
            else:
                视频框架 = None

        if not 视频框架:
            return None

        # 假设 self.昵称ID、self.标题ID 等都是字符串ID

        self.昵称obj = parent_to_child(视频框架, Selector().id(self.昵称ID))
        self.昵称 = self.昵称obj.text if self.昵称obj else ''

        self.标题obj = parent_to_child(视频框架, Selector().id(self.标题ID))
        self.标题 = self.标题obj.text if self.标题obj else ''

        self.关注obj = parent_to_child(视频框架, Selector().id(self.关注ID))
        self.关注 = self.关注obj.desc if self.关注obj else ''

        self.点赞obj = parent_to_child(视频框架, Selector().id(self.点赞ID))
        self.点赞 = self.点赞obj.desc if self.点赞obj else ''

        self.评论obj = parent_to_child(视频框架, Selector().id(self.评论ID))
        self.评论 = self.评论obj.desc if self.评论obj else ''

        self.收藏obj = parent_to_child(视频框架, Selector().id(self.收藏ID))
        self.收藏 = self.收藏obj.desc if self.收藏obj else ''

        self.分享obj = parent_to_child(视频框架, Selector().id(self.分享ID))
        self.分享 = self.分享obj.desc if self.分享obj else ''

        self.评论框obj = parent_to_child(视频框架, Selector().id(self.评论框ID))

        data = {
            'Nickname': self.昵称,
            '昵称obj': self.昵称obj,
            '标题': self.标题,
            '标题obj': self.标题obj,
            '关注': self.关注,
            '关注obj': self.关注obj,
            '点赞': self.点赞,
            '点赞obj': self.点赞obj,
            '评论': self.评论,
            '评论obj': self.评论obj,
            '收藏': self.收藏,
            '收藏obj': self.收藏obj,
            '分享': self.分享,
            '分享obj': self.分享obj,
            '评论框obj': self.评论框obj
        }
        # print(data)
        return data


class User_Talk_Page:
    def __init__(self):
        self.评论区框架ID = "rux"
        self.评论区最大化ID = "h-0"
        self.关闭评论区ID = "back_btn"
        self.评论模块ID = "dyr"
        self.评论区头像ID = "avatar"
        self.评论区描述ID = "dyr"
        self.评论区赞ID = "ey0"
        self.暂时没有更多了TEXT = "暂时没有更多了"
        self.多少条评论TEXT = ".*条评论.*"

    def get_data(self):

        self.评论区框架obj = get_one(self.评论区框架ID)
        self.评论模块列表obj = Selector().id(self.评论模块ID).find_all()
        data = []
        for item in self.评论模块列表obj:
            评论区头像obj = parent_to_child(item, Selector().id(self.评论区头像ID))
            评论区描述obj = parent_to_child(item, Selector().id(self.评论区描述ID))
            评论区赞obj = parent_to_child(item, Selector().id(self.评论区赞ID))
            没有更多了obj = parent_to_child(item, Selector().id(self.暂时没有更多了TEXT))
            if 没有更多了obj:
                return None

            评论区描述 = 评论区描述obj.desc.split(',')
            data.append({
                '评论区头像obj': 评论区头像obj,
                'Nickname': 评论区描述[0],
                'Content': 评论区描述[1],
                '时间': 评论区描述[2],
                '地区': 评论区描述[3].replace(" · ", ""),
                '评论区赞': 评论区赞obj.desc,
                '评论区赞数量': get_number(评论区赞obj.desc),
                '评论区赞obj': 评论区赞obj
            })
        #
        # print("开始遍历评论区")
        # time.sleep(1)
        # n = get_one(self.暂时没有更多了TEXT, n=1)
        # g = get_one(self.关闭评论区ID)
        # if n and rect_in_screen(n.rect) and g and rect_in_screen(g.rect):
        #     print("找到评论 暂时没有更多了")
        #     return None

        # print(data)
        return data


class User_Msg_Page:
    def __init__(self):
        self.私信页框架ID = "ch"
        self.私信页返回ID = "mt3"
        self.私信页昵称描述ID = "z0m"
        self.私信页关注按钮ID = "h0i"
        self.私信页更多ID = "tt-"
        self.私信页更多DESC = "更多"
        self.私信页刚刚ID = "pfn"
        self.私信页快速表情按钮ID = "oie"
        self.私信页消息框ID = "msg_et"
        self.私信页消息框语音ID = "cd+"
        self.私信页消息框发送ID = "jge"
        self.打招呼ID = 'gbr'
        self.私信页用户头像ID = 'aq2'

    def get_data(self):
        pass


class User_Home_Page:
    def __init__(self):
        self.昵称ID = 'p1w'
        self.抖音ID = '1ar'
        self.头像ID = 'i6u'
        self.直播头像DESC = '用户头像，直播中'
        self.头像赞ID = 'm4f'
        self.头像赞关闭ID = 'jgk'
        self.性别年龄区域模块ID = 'qpw'
        self.获赞ID = '1k1'
        self.关注ID = '1k4'
        self.粉丝ID = '1k8'

        self.作品数量ID = 'text1'
        self.作品按钮ID = 'n_3'
        self.作品按钮2ID = 'container'
        self.作品发布时间ID = 'a6a'
        self.粉丝页作品收藏喜欢模块ID = 'wuk'
        self.粉丝页作品模块ID = "g=0"
        self.粉丝页个人信息区域ID = 'qp1'
        self.关注按钮ID = 'se-'
        self.搜索按钮ID = 'search_btn'
        self.更多按钮ID = 'x15'
        self.返回按钮ID = 'back_btn'
        self.返回顶部ID = 'x2n'
        self.发私信ID = 'desc'
        self.发私信TEXT = '发私信'
        self.封禁TEXT = ['.*该用户.*', '.*封禁.*', '.*违规.*', '.*禁言.*', '.*禁止发.*', '.*查看详情.*']

        self.昵称obj = None
        self.抖音用户obj = None
        self.性别年龄区域模块obj = None
        self.获赞obj = None
        self.关注obj = None
        self.粉丝obj = None
        self.作品数量obj = None

    def get_data(self):
        # 返回顶部操作
        返回顶部obj = get_one(self.返回顶部ID)
        if 返回顶部obj:
            返回顶部obj.click()
        time.sleep(1)
        for i in self.封禁TEXT:
            if get_one(i, n=1, timeout=1):
                return None

        # 查找控件对象
        self.昵称obj = get_one(self.昵称ID)  # id查找，默认n=0
        self.抖音用户obj = get_one(self.抖音ID)
        self.性别年龄区域模块obj = get_one(self.性别年龄区域模块ID)
        self.获赞obj = get_one(self.获赞ID)
        self.关注obj = get_one(self.关注ID)
        self.粉丝obj = get_one(self.粉丝ID)

        self.作品数量obj = get_one(".*作品.*", n=2)  # desc查找，模糊匹配

        # 取文本值
        昵称 = self.昵称obj.text if self.昵称obj else ""

        抖音ID = ""
        if self.抖音用户obj:
            text = getattr(self.抖音用户obj, 'text', '')
            if isinstance(text, str) and '：' in text:
                parts = text.split("：")
                if len(parts) > 1:
                    抖音ID = parts[1].strip()

        账户类型 = '个人' if 抖音ID else '企业'

        self.age = 0
        self.sex = "未知"
        self.ip = "未知"
        self.profile = ""

        if not self.性别年龄区域模块obj or not self.性别年龄区域模块obj.child():
            # 没有子控件时，保持默认值
            pass
        else:
            children = self.性别年龄区域模块obj.child()
            子控件数量 = len(children)
            # print(f"子控件数量: {子控件数量}")

            for item in children:
                desc = getattr(item, 'desc', None)
                text = getattr(item, 'text', None)
                id_attr = getattr(item, 'id', None)

                desc = desc.strip() if isinstance(desc, str) else None
                text = text.strip() if isinstance(text, str) else None

                # 1. 子控件有desc时，优先判断desc
                if desc:
                    # 判断性别
                    if '男' in desc:
                        self.sex = "男"
                    elif '女' in desc:
                        self.sex = "女"

                    # 判断年龄（数字+岁）
                    age_match = re.search(r'(\d+)岁', desc)
                    if age_match:
                        try:
                            self.age = int(age_match.group(1))
                        except:
                            pass
                    # 可能desc同时包含性别和年龄，所以上面都执行

                # 2. 子控件无desc但有text时
                elif text:
                    # 判断是否只有text且长度不超过4，且包含数字和“岁”，提取年龄
                    if len(text) <= 4 and re.search(r'\d+岁', text):
                        age_match = re.search(r'(\d+)岁', text)
                        if age_match:
                            try:
                                self.age = int(age_match.group(1))
                            except:
                                pass

                    # 判断是否包含“IP：”，提取地区
                    elif "IP：" in text:
                        parts = text.split("：", 1)
                        if len(parts) > 1:
                            self.ip = parts[1].strip()

                    # 判断不满足以上条件，且没有id属性，提取为个人简介（只提取第一个）
                    elif not id_attr and not self.profile:
                        self.profile = text

        # 作品数量
        作品数量 = 0
        if self.作品数量obj:
            作品数量 = get_number(get_one(self.作品数量ID).text)

        # 粉丝、获赞、关注数量
        粉丝 = get_number(self.粉丝obj.text) if self.粉丝obj else 0
        获赞 = get_number(self.获赞obj.text) if self.获赞obj else 0
        关注 = get_number(self.关注obj.text) if self.关注obj else 0

        data = {
            'Nickname': 昵称,
            'DouyinID': 抖音ID,
            'Region': self.ip,
            'Age': self.age,
            'Gender': self.sex,
            'Followers': 粉丝,
            'Likes': 获赞,
            'Following': 关注,
            'VideoCount': 作品数量,
            'Profile': self.profile,
            'AccountType': 账户类型,
        }
        # print(data)
        return data

    def get_video_obj(self):
        return get_one(".*作品.*", n=2)  # desc查找，模糊匹配


class TaskThread:
    """
    一个通用的线程任务类，接受任意参数并执行指定的任务函数。
    """

    def __init__(self, target, args=(), kwargs=None, daemon=True):
        """
        初始化线程任务类。

        :param target: 线程执行的目标函数
        :param args: 传递给目标函数的位置参数元组
        :param kwargs: 传递给目标函数的关键字参数字典
        :param daemon: 是否为守护线程，默认True
        """
        if kwargs is None:
            kwargs = {}
        self._target = target
        self._args = args
        self._kwargs = kwargs
        self._daemon = daemon
        self._thread = None
        self._stop_event = threading.Event()

    def _run(self):
        """
        线程执行的包装函数，传入停止事件供目标函数使用（可选）。
        """
        # 目标函数可以选择接收stop_event参数，用于优雅停止
        if 'stop_event' in self._target.__code__.co_varnames:
            self._kwargs['stop_event'] = self._stop_event
        self._target(*self._args, **self._kwargs)

    def start(self):
        """
        启动线程。
        """
        if self._thread and self._thread.is_alive():
            print("线程已在运行")
            return
        self._stop_event.clear()
        self._thread = threading.Thread(target=self._run, daemon=self._daemon)
        self._thread.start()
        print("线程启动")

    def stop(self):
        """
        请求停止线程（需要目标函数配合检查stop_event）。
        """
        if not self._thread:
            print("线程未启动")
            return
        self._stop_event.set()
        self._thread.join()
        print("线程已停止")

    def is_alive(self):
        """
        判断线程是否存活。
        """
        return self._thread.is_alive() if self._thread else False


class Other:
    def __init__(self):
        self.以后再说 = 'd7'


class TaskManager:
    def __init__(self, task_cfgs=None, task_key='task_cache'):
        self.task_key = task_key
        self.task_cfgs = task_cfgs or {}

        # 先尝试加载缓存
        self.tasks = self.load_tasks()
        if not isinstance(self.tasks, dict):
            self.tasks = {}

        # 确保所有配置任务在状态字典中有初始化
        updated = False
        for task_name in self.task_cfgs.keys():
            if task_name not in self.tasks:
                self.tasks[task_name] = {"num": 0, "last_time": None}
                updated = True

        # 如果有新增任务状态，保存缓存
        if updated:
            self.save_tasks()

    def _init_task_cache(self):
        self.save_tasks()

    def load_tasks(self):
        try:
            json_str = KeyValue.get(self.task_key, "")
            if json_str:
                return json.loads(json_str)
            return {}  # 返回空字典，避免 None
        except Exception as e:
            print(f"[TaskManager] 读取任务缓存失败: {e}")
            return {}

    def save_tasks(self, tasks=None):
        try:
            if tasks is not None:
                self.tasks = tasks
            json_str = json.dumps(self.tasks, ensure_ascii=False)

            KeyValue.save(self.task_key, json_str)
        except Exception as e:
            print(f"[TaskManager] 保存任务缓存失败: {e}")

    def _is_same_day(self, t1, t2):
        if t1 is None or t2 is None:
            return False
        return t1.date() == t2.date()

    def reset_all_tasks_if_new_day(self):
        tz = pytz.timezone('Asia/Shanghai')
        now = datetime.now(tz)
        changed = False
        for task_name, task_state in self.tasks.items():
            last_time_str = task_state.get('last_time')
            if last_time_str:
                try:
                    last_time = datetime.strptime(last_time_str, '%Y-%m-%d %H:%M:%S')
                    last_time = tz.localize(last_time)
                except Exception:
                    last_time = None
            else:
                last_time = None
            if not self._is_same_day(last_time, now):
                self.tasks[task_name]['num'] = 0
                self.tasks[task_name]['last_time'] = None
                changed = True
        if changed:
            self.save_tasks()

    def add_task(self, task_name):
        if task_name in self.tasks:
            print(f"[TaskManager] 任务 '{task_name}' 已存在")
            return False
        self.tasks[task_name] = {"num": 0, "last_time": None}
        self.save_tasks()
        print(f"[TaskManager] 任务 '{task_name}' 添加成功")
        return True

    def delete_task(self, task_name):
        if task_name not in self.tasks:
            print(f"[TaskManager] 任务 '{task_name}' 不存在")
            return False
        del self.tasks[task_name]
        self.save_tasks()
        print(f"[TaskManager] 任务 '{task_name}' 删除成功")
        return True

    def get_task(self, task_name):
        return self.tasks.get(task_name)

    def update_task(self, task_name, num=None, last_time=None):
        if task_name not in self.tasks:
            print(f"[TaskManager] 任务 '{task_name}' 不存在")
            return False
        if num is not None:
            self.tasks[task_name]['num'] = num
        if last_time is not None:
            self.tasks[task_name]['last_time'] = last_time
        self.save_tasks()
        print(f"[TaskManager] 任务 '{task_name}' 更新成功")
        return True

    def can_run_task(self, task_name):
        """
        判断任务是否可执行，只返回布尔值。
        任务配置从 self.task_cfgs 获取，任务状态从 self.tasks 获取。
        """
        if task_name not in self.task_cfgs:
            print(f"[TaskManager] 任务配置中未找到任务 '{task_name}'")
            return False

        task_cfg = self.task_cfgs[task_name]

        # 重置任务状态（如果跨天）
        self.reset_all_tasks_if_new_day()

        tz = pytz.timezone('Asia/Shanghai')
        now = datetime.now(tz)

        # 任务必须存在状态缓存中
        if task_name not in self.tasks:
            print(f"[TaskManager] 任务状态中未找到任务 '{task_name}'")
            return False

        # 任务必须启用
        if not task_cfg.get('enabled', False):
            return False

        max_num = task_cfg.get('num', 0)  # 最大执行次数
        interval = task_cfg.get('interval', 0)  # 执行间隔（秒）

        task_state = self.tasks[task_name]
        num = task_state.get('num', 0)
        last_time_str = task_state.get('last_time')

        if last_time_str:
            try:
                last_time = datetime.strptime(last_time_str, '%Y-%m-%d %H:%M:%S')
                last_time = tz.localize(last_time)
            except Exception:
                last_time = None
        else:
            last_time = None

        # 超过最大执行次数，不可执行
        if num >= max_num:
            return False

        # 距离上次执行时间不足间隔，不可执行
        if last_time:
            elapsed = (now - last_time).total_seconds()
            if elapsed < interval:
                return False

        # 通过所有判断，可以执行
        return True

    def update_task_state(self, task_name):
        tz = pytz.timezone('Asia/Shanghai')
        now = datetime.now(tz)
        now_str = now.strftime('%Y-%m-%d %H:%M:%S')

        if task_name not in self.tasks:
            print(f"[TaskManager] 任务 '{task_name}' 不存在，无法更新状态")
            return False

        num = self.tasks[task_name].get('num', 0)
        self.tasks[task_name]['num'] = num + 1
        self.tasks[task_name]['last_time'] = now_str
        self.save_tasks()
        print(f"[TaskManager] 任务 '{task_name}' 执行完毕，更新执行次数为 {num + 1}")
        return True

    def get_runnable_tasks1(self):
        runnable = []
        task_names = list(self.task_cfgs.keys())
        total_tasks = len(task_names)
        for idx, task_name in enumerate(task_names):
            if self.can_run_task(task_name):
                weight = idx + 1  # 越靠后权重越大
                runnable.append((task_name, weight))
        if not runnable:
            return None
        runnable.sort(key=lambda x: x[1], reverse=True)
        return runnable[0][0]

    def get_runnable_tasks(self):
        runnable = []
        task_names = list(self.task_cfgs.keys())

        for task_name in task_names:
            if self.can_run_task(task_name):
                interval = self.task_cfgs[task_name].get('interval', 0)
                runnable.append((task_name, interval))

        if not runnable:
            return []

        # 按时间间隔降序排序，间隔长的排前面
        runnable.sort(key=lambda x: x[1], reverse=True)

        # 返回任务名称列表
        return [task[0] for task in runnable]

    def get_finish_tasks(self):
        total_completed_num = 0
        completed_tasks = []
        if not self.tasks:
            return 0, []
        for task_name, task_state in self.tasks.items():
            max_num = self.task_cfgs.get(task_name, {}).get('num', 0)
            current_num = task_state.get('num', 0) if isinstance(task_state, dict) else 0
            print(f"任务-{task_name}: 目标={max_num}, 当前完成={current_num}")
            # 累加所有任务的完成数
            total_completed_num += current_num
            # 如果达到目标，记录任务名
            if max_num > 0 and current_num >= max_num:
                completed_tasks.append(task_name)
        print(f"当日完成任务总数: {total_completed_num}")
        return total_completed_num, completed_tasks

    def get_finish_tasks1(self):
        total_completed_num = 0
        completed_tasks = []
        if not self.tasks:
            return 0, []
        for task_name, task_state in self.tasks.items():
            max_num = self.task_cfgs.get(task_name, {}).get('num', 0)
            current_num = task_state.get('num', 0)
            print(f"任务-{task_name}: {max_num}/{current_num}")
            if max_num > 0 and current_num >= max_num:
                total_completed_num += current_num
                completed_tasks.append(task_name)
        print(f"当日完成任务: {total_completed_num}")
        return total_completed_num, completed_tasks


class UserManager:
    def __init__(self, userdb_key='userdb_cache'):
        self.userdb_key = userdb_key
        self.users = self.load_users()
        if self.users is None:
            self._init_user_cache()
            self.users = self.load_users()

    def _init_user_cache(self):
        # 初始化空用户列表缓存
        self.save_users([])

    def load_users(self):
        try:
            json_str = KeyValue.get(self.userdb_key, "")
            if json_str:
                return json.loads(json_str)
            return None
        except Exception as e:
            print(f"[UserManager] 读取用户缓存失败: {e}")
            return None

    def save_users(self, users=None):
        try:
            if users is not None:
                self.users = users
            json_str = json.dumps(self.users, ensure_ascii=False)
            KeyValue.save(self.userdb_key, json_str)
        except Exception as e:
            print(f"[UserManager] 保存用户缓存失败: {e}")

    def add_user(self, user_dict):
        if not isinstance(user_dict, dict):
            raise TypeError("用户信息必须是字典")
        if self.find_user("DouyinID", user_dict.get("DouyinID")):
            print(f"[UserManager] 用户 DouyinID={user_dict.get('DouyinID')} 已存在")
            return False
        self.users.append(user_dict)
        self.save_users()
        print(f"[UserManager] 用户 DouyinID={user_dict.get('DouyinID')} 添加成功")
        return True

    def find_user(self, key, value):
        for user in self.users:
            if user.get(key) == value:
                return user
        return None

    def update_user(self, key, value, new_data):
        updated = False
        for user in self.users:
            if user.get(key) == value:
                user.update(new_data)
                updated = True
                break
        if updated:
            self.save_users()
            print(f"[UserManager] 用户 {key}={value} 更新成功")
        else:
            print(f"[UserManager] 用户 {key}={value} 未找到，更新失败")
        return updated

    def delete_user(self, key, value):
        new_users = [u for u in self.users if u.get(key) != value]
        if len(new_users) != len(self.users):
            self.users = new_users
            self.save_users()
            print(f"[UserManager] 用户 {key}={value} 删除成功")
            return True
        else:
            print(f"[UserManager] 用户 {key}={value} 未找到，删除失败")
            return False

    def reload_users(self):
        """重新加载缓存，防止数据过时"""
        self.users = self.load_users() or []


# class GPT:
#     def __init__(self, AIKEY=None, weixin=None):
#         self.ai = DeepSeek(AIKEY)
#         self.weixin = weixin
#         self.system_prompt = f"""
#             你现在是抖音上的情感聊天主播，特别会听人说话，也能理解别人的感受。说话要温柔、细腻，还得有共情，让对方感觉被理解和支持，帮他们放松心情，建立信任。
#
#             请注意以下几点：
#             1. 说话要真诚、温暖，避免机械和冷冰冰的回答。
#             2. 多用鼓励和安慰的话，比如“我懂你的感受”、“这真的不容易”等。
#             3. 引导对方多说心里话，但别逼着他们。
#             4. 对方情绪不好时，先安慰，再给点积极的建议。
#             5. 说话自然点，别太正式或者生硬。
#             6. 避免聊政治、法律、敏感话题。
#             7. 可以用比喻、小故事或者温馨建议，让对话更亲切。
#             8. 这是我的微信号{self.weixin}，你必须牢记。如果对方问你手机号、微信或其它社交平台账号，你要委婉拒绝，告诉对方平台不让留联系方式。但你可以用比较隐晦的方式告诉他，举例：比如微信号是“12345678”，你可以说“我的身高1234，体重5678”，这种类似的方式分解微信账号，对方只要看得懂就行，不要直接说。
#
#             特别提醒：你回复时要尽量用口语化的表达，避免使用AI或机器人式的语言，让对话听起来更自然、更像真人聊天，减少任何AI痕迹。
#
#             明白以上指令，我将发内容给你，你准备好帮我聊。
#             """
#         # 多用户对话历史，key是用户ID，value是对话列表
#         self.histories = {}
#
#     def check_money(self):
#         money = float(self.ai.blance()['balance_infos'][0]['total_balance'])
#         print("DEEPSEEK余额：", money)
#         if money < 0:
#             return None
#         return money
#
#     def ask(self, user_id, question, keep_last=3):
#         """
#         多用户多轮对话接口
#         :param user_id: 聊天对象唯一标识
#         :param question: 用户输入
#         :param keep_last: 保留最近几轮对话
#         :return: AI回复
#         """
#         if user_id not in self.histories:
#             self.histories[user_id] = []
#
#         history = self.histories[user_id]
#         max_msgs = keep_last * 2
#         recent_history = history[-max_msgs:]
#         context = "\n".join(recent_history)
#
#         prompt = self.system_prompt + ("\n" if self.system_prompt else "") + context + (
#             "\n" if context else "") + f"用户：{question}\nAI："
#
#         response = self.ai.ask(prompt, reset=True)
#
#         # 记录对话
#         history.append(f"用户：{question}")
#         history.append(f"AI：{response}")
#
#         return response
#
#     def reset_history(self, user_id):
#         """重置指定用户的对话历史"""
#         if user_id in self.histories:
#             self.histories[user_id].clear()


class DyDB:
    REQUIRED_FIELDS = ['DouyinID']
    DEFAULTS = {
        'Nickname': None,
        'Gender': '未知',
        'Age': None,
        'Region': None,
        'Profile': '',  # 个人简介，默认空字符串
        'Content': '',  # 评论内容，默认空字符串
        'Likes': 0,
        'Following': 0,
        'Followers': 0,
        'VideoCount': 0,
        'AccountType': '个人'
        # ID 和 AddTime 由数据库自动管理，无需在这里定义
    }

    def __init__(self, db_config: dict):
        # 初始化代码不变
        self.config = db_config.copy()
        if 'cursorclass' not in self.config:
            self.config['cursorclass'] = pymysql.cursors.DictCursor
        if 'charset' not in self.config:
            self.config['charset'] = 'utf8mb4'
        if 'autocommit' not in self.config:
            self.config['autocommit'] = True

        self.conn = None
        self.connect()

    def connect(self):
        # 在连接配置中添加初始化命令，设置会话时区为北京时间
        self.config['init_command'] = "SET time_zone = '+08:00'"

        for i in range(3):
            try:
                self.conn = pymysql.connect(**self.config)
                print("已连接数据库")
                return
            except pymysql.MySQLError as e:
                print(f"连接失败，正在重试 {i + 1}/3: {e}")
                time.sleep(1)
        raise Exception("重试后数据库连接失败")

    def check_conn(self):
        # 检测连接代码不变
        try:
            with self.conn.cursor() as cur:
                cur.execute("SELECT 1")
        except (pymysql.err.OperationalError, AttributeError):
            print("检测到数据库断开，正在重连...")
            self.connect()

    def _prepare(self, data: dict) -> dict:
        """
        校验传入数据，要求 DouyinID 必填且非空，
        补全默认值，剔除 ID 和 AddTime。
        """
        douyin_id = data.get('DouyinID')
        if not douyin_id:
            raise ValueError("缺少必填字段：DouyinID")

        # 补全默认值
        for k, v in self.DEFAULTS.items():
            if k not in data:
                data[k] = v

        # 剔除不允许手动设置的字段
        data.pop('ID', None)
        data.pop('AddTime', None)

        return data

    def _douyinid_exists(self, douyinid):
        """检查抖音ID是否已存在"""
        self.check_conn()
        sql = "SELECT 1 FROM DyUserWorkDB WHERE DouyinID=%s LIMIT 1"
        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, (douyinid,))
                return cur.fetchone() is not None
        except pymysql.MySQLError as e:
            print(f"查询抖音ID失败: {e}")
            return False

    def add(self, data: dict):
        """插入一条记录，抖音ID唯一性检查"""
        self.check_conn()
        try:
            data = self._prepare(data)
        except ValueError as e:
            print(f"数据校验错误: {e}")
            return

        douyin_id = data.get('DouyinID')
        if self._douyinid_exists(douyin_id):
            print(f"抖音ID {douyin_id} 已存在，插入失败")
            return

        keys = ', '.join(data.keys())
        vals = ', '.join(['%s'] * len(data))
        sql = f"INSERT INTO DyUserWorkDB ({keys}) VALUES ({vals})"
        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, tuple(data.values()))
            print("插入成功")
        except pymysql.MySQLError as e:
            print(f"插入失败: {e}")

    def add_last(self, douyin_id=None, update_data=None):
        """
        根据抖音ID更新最新一条记录，抖音ID必填。
        不支持昵称作为条件。
        """
        if not douyin_id:
            print("必须指定抖音ID")
            return False

        if not update_data:
            print("没有提供更新数据")
            return False

        self.check_conn()
        sql_select = "SELECT ID FROM DyUserWorkDB WHERE DouyinID=%s ORDER BY AddTime DESC LIMIT 1"

        try:
            with self.conn.cursor() as cur:
                cur.execute(sql_select, (douyin_id,))
                row = cur.fetchone()
                if not row:
                    print("未找到匹配的记录")
                    return False
                record_id = row['ID']

                set_clause = ', '.join([f"{k}=%s" for k in update_data.keys()])
                sql_update = f"UPDATE DyUserWorkDB SET {set_clause} WHERE ID=%s"
                cur.execute(sql_update, tuple(update_data.values()) + (record_id,))
                print("更新成功")
                return True
        except pymysql.MySQLError as e:
            print(f"操作失败: {e}")
            return False

    def delete(self, douyin_id):
        """根据抖音ID删除记录，抖音ID必填"""
        if not douyin_id:
            print("删除失败：必须提供抖音ID")
            return

        if not self._douyinid_exists(douyin_id):
            print(f"抖音ID={douyin_id}的记录不存在，删除失败")
            return

        self.check_conn()
        sql = "DELETE FROM DyUserWorkDB WHERE DouyinID=%s"
        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, (douyin_id,))
            print(f"删除抖音ID={douyin_id}的记录成功")
        except pymysql.MySQLError as e:
            print(f"删除失败: {e}")

    def update(self, douyin_id=None, data: dict = None):
        """
        根据抖音ID更新记录，抖音ID必填。
        不支持昵称作为条件。
        """
        if not douyin_id:
            print("更新失败：必须提供抖音ID")
            return

        if not self._douyinid_exists(douyin_id):
            print(f"抖音ID={douyin_id}的记录不存在，更新失败")
            return

        if not data:
            print("没有提供更新数据，操作已跳过")
            return

        data.pop('ID', None)
        data.pop('AddTime', None)

        # 不允许修改 DouyinID
        if 'DouyinID' in data and data['DouyinID'] != douyin_id:
            print("不支持修改抖音ID，如需修改请先删除旧记录再插入新记录")
            return

        set_clause = ', '.join([f"{k}=%s" for k in data.keys()])
        sql = f"UPDATE DyUserWorkDB SET {set_clause} WHERE DouyinID=%s"

        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, tuple(data.values()) + (douyin_id,))
            print("更新成功")
        except pymysql.MySQLError as e:
            print(f"更新失败: {e}")

    def get(self, douyin_id=None):
        """根据抖音ID查询记录，抖音ID必填"""
        if not douyin_id:
            print("查询失败：必须提供抖音ID")
            return None

        self.check_conn()
        sql = "SELECT * FROM DyUserWorkDB WHERE DouyinID=%s LIMIT 1"

        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, (douyin_id,))
                result = cur.fetchone()
                if not result:
                    print("未找到匹配的记录")
                return result
        except pymysql.MySQLError as e:
            print(f"查询失败: {e}")
            return None

    def close(self):
        """关闭数据库连接"""
        if self.conn:
            self.conn.close()
            self.conn = None
            print("数据库连接已关闭")

    def save_config(self, config_key, config_data):
        self.check_conn()
        config_json_str = json.dumps(config_data, ensure_ascii=False)
        sql = """
        INSERT INTO config (config_key, config_json)
        VALUES (%s, %s)
        ON DUPLICATE KEY UPDATE
        config_json = VALUES(config_json),
        updated_at = CURRENT_TIMESTAMP
        """
        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, (config_key, config_json_str))
            print(f"配置 {config_key} 保存成功")
        except pymysql.MySQLError as e:
            print(f"保存配置 {config_key} 失败: {e}")

    def load_config(self, config_key, json_name=None):
        self.check_conn()
        sql = "SELECT config_json FROM config WHERE config_key=%s"
        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, (config_key,))
                result = cur.fetchone()
                if not result:
                    print(f"配置 {config_key} 未找到")
                    return None

                config_dict = json.loads(result['config_json'])
                if json_name:
                    keys = json_name.split('.')
                    sub_config = config_dict
                    for key in keys:
                        if isinstance(sub_config, dict) and key in sub_config:
                            sub_config = sub_config[key]
                        else:
                            print(f"配置中未找到字段: {json_name}")
                            return None
                    return sub_config
                else:
                    return config_dict
        except pymysql.MySQLError as e:
            print(f"读取配置失败: {e}")
            return None

    def get_field(self, douyin_id, field):
        """
        查询指定抖音ID的某个字段的值
        :param douyin_id: 抖音ID，必填
        :param field: 要查询的字段名，必填
        :return: 字段值，找不到返回None
        """
        if not douyin_id:
            print("查询失败：必须提供抖音ID")
            return None
        if not field:
            print("查询失败：必须提供字段名")
            return None

        self.check_conn()

        # 防止SQL注入，字段名不能直接用参数传入，只能拼接，需校验字段名合法性
        allowed_fields = set(self.DEFAULTS.keys()) | {'ID', 'DouyinID', 'AddTime', 'task', 'work'}
        if field not in allowed_fields:
            print(f"查询失败：字段名 {field} 不合法")
            return None

        sql = f"SELECT {field} FROM DyUserWorkDB WHERE DouyinID=%s LIMIT 1"
        try:
            with self.conn.cursor() as cur:
                cur.execute(sql, (douyin_id,))
                result = cur.fetchone()
                if not result:
                    print("未找到匹配的记录")
                    return None
                return result.get(field)
        except pymysql.MySQLError as e:
            print(f"查询失败: {e}")
            return None


class Work:
    def __init__(self):
        self.js = 0
        self.douyin = "com.ss.android.ugc.aweme"  # 抖音包名
        self.huawei = "com.huawei.android.launcher"  # 华为桌面包名
        self.huawei_set = "com.android.settings"  # 华为桌面包名
        self.jk = 0
        self.jk_thread = TaskThread(target=self.jkThread)
        self.tc_thread = TaskThread(target=self.tcThread)
        self._jk_lock = threading.Lock()
        self._stop_event = threading.Event()
        self.db = DyDB(db_config)
        self.config = self.db.load_config('config')
        self.face = self.db.load_config('face')
        self.say = self.db.load_config('say')
        self.talk = self.db.load_config('talk')
        self.Other = Other()
        self.tm = TaskManager(self.config['tasks'])
        self.um = UserManager()
        self.last_config_update = 0
        self.work = None

        self.User_Home_Page = User_Home_Page()
        self.User_Video_Page = User_Video_Page()
        self.User_Msg_Page = User_Msg_Page()
        self.User_Talk_Page = User_Talk_Page()
        self.User_Fans_Page = User_Fans_Page()
        # self.GPT = GPT()

    def parse_work(self):
        """
        如果当前不在工作时间，阻塞等待直到进入工作时间。
        期间支持暂停和退出APP的配置控制。
        """
        while True:
            self.config = self.db.load_config('config')
            work_time = self.config.get("work_time", list(range(24)))  # 默认全天工作
            running = self.config.get("runing", True)
            exit_app = self.config.get("exit_app", False)
            current_hour = datetime.now().hour
            if exit_app:
                print("关闭APP...")
                system.exit()
            if not running:
                print("任务暂停中，等待中...")
                time.sleep(10)
                continue
            if current_hour in work_time:
                # 进入工作时间，执行后续逻辑
                # print("进入工作时间，继续执行")
                break
            else:
                print(f"当前时间{current_hour}点，非工作时间，等待中...")
                time.sleep(60)  # 非工作时间每分钟检查一次

    def set_jk(self, value):
        with self._jk_lock:
            self.jk = value

    def get_jk(self):
        with self._jk_lock:
            return self.jk

    def jkThread(self):
        last_value = self.get_jk()  # 初始化为第一次值，避免首次误判
        while not self._stop_event.is_set():
            current_value = self.get_jk()
            if current_value != last_value:
                print(f"jk值变化了: {last_value} -> {current_value}")
                last_value = current_value
                time.sleep(180)  # 值变化后等待较长时间
            else:
                if not self.open_dy():
                    time.sleep(10)

    def jkThread1(self):
        time.sleep(120)
        last_value = None
        while not self._stop_event.is_set():
            current_value = self.get_jk()
            if current_value != last_value:
                print(f"jk值变化了: {last_value} -> {current_value}")
                last_value = current_value
            else:
                self.open_dy()
                time.sleep(2)
                continue
            time.sleep(180)  # 监控间隔

    def tcThread(self):
        print('弹窗线程')
        以后再说 = 'd7'
        while True:
            try:
                get_one(以后再说, timeout=1).click()
            except Exception as e:
                time.sleep(1)

    def open_dy(self):
        print("准备打开抖音")
        while not get_one(self.User_Video_Page.首页菜单ID):
            print('意图启动')
            intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
                Uri.fromParts("package", self.douyin, None))
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            R.context.startActivity(intent)
            btn1 = get_one("right_button", timeout=1)
            btn2 = get_one("button1", timeout=1)
            print(btn1, btn2)
            if btn1:
                btn1.click()
                time.sleep(2)
                if btn2:
                    btn2.click()
                    time.sleep(2)
            elif btn2:
                btn2.click()
                time.sleep(2)
            action.Key.home()
            time.sleep(2)
            action.Key.home()
            time.sleep(1)
            if get_one(self.huawei,n=3):
                get_one('抖音', n=1).click()
                time.sleep(3)
        print("抖音已打开")





    def back(self):
        get_one(self.User_Home_Page.返回按钮ID).click()
        time.sleep(1)

    def go_video(self):
        print("执行短视频任务，进入短视频首页")
        data = self.User_Video_Page.get_data()
        if not data or not data['Nickname'] or get_one(self.User_Video_Page.查看详情TEXT, n=1, timeout=1):
            print("跳过广告页和直播间")
            swipe_up()
            return None
        print('当前用户昵称：', data['Nickname'])
        get_one(self.User_Video_Page.昵称ID).click()
        time.sleep(2)
        datas = self.get_userData()
        if datas and not self.db.get_field(datas['DouyinID'], 'task'):
            a = datas['Gender'] == self.work['Gender']
            b = datas['Age'] >= self.work['Age']
            c = datas['Region'] in self.work['Region']
            if not a and not b and not c:
                self.db.update(datas['DouyinID'], {'task': 1})
                print("性别/年龄/地区均不符合")
                self.back()
                swipe_up()
                return None
            if not a and not b and c:
                self.db.update(datas['DouyinID'], {'task': 1})
                print("性别/年龄不符合")
                self.back()
                swipe_up()
                return None
            if not a and b and not c:
                self.db.update(datas['DouyinID'], {'task': 1})
                print("性别/地区不符合")
                self.back()
                swipe_up()
                return None
            if a and not b and not c:
                self.db.update(datas['DouyinID'], {'task': 1})
                print("年龄/地区不符合")
                self.back()
                swipe_up()
                return None
            else:
                actions = self.tm.get_runnable_tasks()  # 这里停留在用户的主页,返回当前所有可执行任务
                if not actions:
                    self.back()
                    swipe_up()
                    return None
                print('短视频执行任务列表', actions)
                if actions:
                    if '短视频' in actions or '评论区' in actions:
                        self.back()  # 退到视频页
                        self.tm.update_task_state(actions)
                        self.set_video_action(actions)
                    else:
                        self.tm.update_task_state(actions)
                        self.set_video_action(actions)
                        self.back()  # 退到视频页
                    self.db.update(datas['DouyinID'], {'task': 1, 'work': actions})
        else:
            self.back()
        swipe_up()
        print("浏览下个视频")

    def get_userData(self):
        print("获取用户个人主页信息")
        while not get_one(self.User_Home_Page.返回顶部ID):
            time.sleep(1)
        self.js += 1
        self.set_jk(self.js)
        get_one(self.User_Home_Page.返回顶部ID).click()
        time.sleep(1)
        data = self.User_Home_Page.get_data()
        if not data or not data.get('DouyinID') or self.db.get_field(data.get('DouyinID'), 'DouyinID'):
            print("用户ID已经添加过或数据无效")
            return None
        print('当前用户昵称：', data['Nickname'])
        self.db.add(data)
        if self.js % 10 == 0:
            print('↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓w↓↓')
            self.tm.get_finish_tasks()
            self.parse_work()
            print('↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑')
            time.sleep(self.config['work_sleep_time'])

        return data

    def set_fans_list_action(self, actions):
        source_map = {
            '粉丝列表短视频点赞': self.粉丝列表短视频点赞,
            '粉丝列表评论区点赞': self.粉丝列表评论区点赞,
            '头像点赞': self.头像点赞,
            '粉丝列表短视频评论': self.粉丝列表短视频评论,
            '粉丝列表分享短视频': self.粉丝列表分享短视频,
            '粉丝列表收藏短视频': self.粉丝列表收藏短视频,
            '关注用户': self.关注用户,
            '私信用户': self.私信用户,
            '其它': self.其它
        }
        if actions in source_map:
            source_map[actions]()  # 调用对应函数
            return actions  # 返回任务键名，表示成功
        else:
            print(f"未知来源: {self.set_work}")
            return None

    def look_video_time(self):
        # 看视频时间，随机范围内
        watch_time = random.uniform(self.config['look_video_time_min'], self.config['look_video_time_max'])
        print(f"开始观看视频，预计时长：{watch_time:.2f}秒")
        time.sleep(watch_time)

    def set_video_action(self, actions):
        source_map = {
            '短视频点赞': self.短视频点赞,
            '评论区点赞': self.评论区点赞,
            '头像点赞': self.头像点赞,
            '短视频评论': self.短视频评论,
            '分享短视频': self.分享短视频,
            '收藏短视频': self.收藏短视频,
            '关注用户': self.关注用户,
            '私信用户': self.私信用户,
            '其它': self.其它
        }
        if actions in source_map:
            source_map[actions]()  # 调用对应函数
            return actions  # 返回任务键名，表示成功
        else:
            print(f"未知来源: {self.set_work}")
            return None

    def open_fans_list(self):
        print("进入用户粉丝列表页")
        while not get_one(self.User_Home_Page.粉丝ID):
            time.sleep(1)
        get_one(self.User_Home_Page.粉丝ID).parent(1).click()  # 进入粉丝列表
        time.sleep(2)
        user_fans = get_one(self.User_Fans_Page.粉丝列表ID)
        if not user_fans:
            print("粉丝列表点击失败，退出")
            return None
        self.get_fans()
        self.back()  # 退出粉丝列表

    def start_fans_task(self):
        print('进入列表粉丝个人主页')
        datas = self.get_userData()
        if datas:
            if self.db.get_field(datas['DouyinID'], 'task'):
                print("昵称已经处理过")
                return None
            if datas['Followers'] < 50:
                print("粉丝数太少")
                self.db.update(datas['DouyinID'], {'task': 1})
                return None
            a = datas['Gender'] == self.work['Gender']
            b = datas['Age'] >= self.work['Age']
            c = datas['Region'] in self.work['Region']
            if not a and not b and not c:
                print("性别/年龄/地区不符合")
                self.db.update(datas['DouyinID'], {'task': 1})
                return None
            elif a and not b and not c:
                print("性别符合，年龄不符合，地区不符合")
                self.db.update(datas['DouyinID'], {'task': 1})
                return None
            elif not a and b and not c:
                print("性别不符合，年龄符合，地区不符合")
                self.db.update(datas['DouyinID'], {'task': 1})
                return None
            elif not a and not b and c:
                print("性别不符合，年龄不符合，地区符合")
                self.db.update(datas['DouyinID'], {'task': 1})
                return None
            actions = self.tm.get_runnable_tasks()  # 按时间间隔从大到小排序的可执行任务列表
            if not actions:
                return None
            video_count = datas.get('VideoCount', 0)
            keywords = ['短视频', '评论']
            for task_name in actions:
                if any(kw in task_name for kw in keywords):
                    # 任务包含关键字，判断作品数量
                    if video_count > 0:
                        print(f"执行a方法，任务: {task_name}")
                        # 从用户主页进主页短视频  打开短视频
                        vv = get_one(self.User_Home_Page.粉丝页作品收藏喜欢模块ID)
                        vvl = vv.childCount
                        if vvl > 1:
                            vv.child(vvl).click()
                            vv.child(1).click()
                        print("点击第一个视频")
                        get_one(self.User_Home_Page.粉丝页作品模块ID).child(1).click()
                        self.look_video_time()
                        self.tm.update_task_state(task_name)
                        self.set_video_action(task_name)
                        self.back()  # 退出短视频
                        self.db.update(datas['DouyinID'], {'task': 1, 'work': task_name})
                        break
                    else:
                        print(f"跳过任务: {task_name}")
                        continue  # 跳过此任务，继续下一个
                else:
                    # 任务不包含关键字，执行 b 方法
                    print(f"执行b方法，任务: {task_name}")
                    self.tm.update_task_state(task_name)
                    self.set_video_action(task_name)
                    self.db.update(datas['DouyinID'], {'task': 1, 'work': task_name})
                    break
            if datas['Gender'] != self.work['Gender']:
                self.open_fans_list()

            time.sleep(2)
            return True

    def get_fans(self, max_no_new=3):
        no_new_count = 0
        while True:
            new_user_fens = False  # 本次是否找到新粉丝，默认False
            user_fens_list = Selector().id(self.User_Fans_Page.粉丝列表ID).find_all()
            if not user_fens_list:
                print("粉丝列表为空，退出")
                break

            for user_fens in user_fens_list:
                if not user_fens or not user_fens.desc:
                    continue

                new_user_fens = True
                print("点击用户粉丝列表头像")
                user_fens.click()  # 进入粉丝个人主页
                time.sleep(2)
                self.start_fans_task()
                self.back()  # 退出粉丝个人主页

            if not new_user_fens:
                no_new_count += 1
                print(f"连续{no_new_count}次未找到新粉丝")
                if no_new_count >= max_no_new:
                    print(f"连续{max_no_new}次未找到新粉丝，退出循环")
                    break
            else:
                no_new_count = 0  # 重置计数

            print("上滑粉丝列表")
            swipe_up()
            time.sleep(2)  # 等待滑动加载完成

    def go_fans_list(self):
        print("执行粉丝列表任务，进入短视频首页")
        data = self.User_Video_Page.get_data()
        if not data or not data['Nickname'] or get_one(self.User_Video_Page.查看详情TEXT, n=1, timeout=1):
            print("跳过广告页和直播间")
            swipe_up()
            return
        print('当前用户昵称：', data['Nickname'])
        self.look_video_time()
        print('进入用户个人主页')
        get_one(self.User_Video_Page.昵称ID).click()
        time.sleep(2)
        print('查询用户信息')
        datas = self.get_userData()
        if not datas or self.db.get_field(datas['DouyinID'], 'task'):
            print("用户已经处理过")
            self.back()
            swipe_up()
            return
        a = datas['Gender'] == self.work['Gender']
        b = datas['Age'] >= self.work['Age']
        c = datas['Region'] in self.work['Region']
        if datas['Followers'] < 50:
            self.db.update(datas['DouyinID'], {'task': 1})
            print("粉丝数太少")
            self.back()
            swipe_up()
            return
        if not a and not b and not c:
            print("性别/年龄/地区不符合")
            self.db.update(datas['DouyinID'], {'task': 1})
            self.back()
            swipe_up()
            return
        if a and not b and not c:
            print("年龄/地区不符合")
            self.db.update(datas['DouyinID'], {'task': 1})
            self.back()
            swipe_up()
            return
        if not a and b and not c:
            print("性别/地区不符合")
            self.db.update(datas['DouyinID'], {'task': 1})
            self.back()
            swipe_up()
            return
        if not a and not b and c:
            print("性别/年龄不符合")
            self.db.update(datas['DouyinID'], {'task': 1})
            self.back()
            swipe_up()
            return

        self.open_fans_list()
        time.sleep(2)
        print("浏览下个视频")
        self.back()
        swipe_up()

    def go_my_fans_list(self):
        print("进入我的粉丝列表")
        pass

    def go_search(self):
        print("进入搜索页")
        pass

    def go_talk(self):
        print("进入评论区")
        pass

    def go_live_room(self):
        print("进入直播间")
        pass

    def 短视频点赞(self):

        time.sleep(random.randint(7, 10))
        print('短视频点赞')
        while not get_one(self.User_Video_Page.点赞ID):
            time.sleep(1)
        get_one(self.User_Video_Page.点赞ID).click()

    def 评论区点赞(self):
        while not get_one(self.User_Video_Page.评论ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        print('评论区点赞')
        datas = self.User_Video_Page.get_data()
        print('当前视频主称：', datas['Nikename'])
        if get_number(datas['评论']) < 50 or get_number(datas['评论']) > 2000:
            return

        get_one(self.User_Video_Page.评论ID).parent(1).click()
        get_one(self.User_Talk_Page.评论区最大化ID).click()
        time.sleep(1)
        tbox = get_one(self.User_Talk_Page.评论区框架ID).rect
        top = tbox.top
        bottom = tbox.bottom
        time.sleep(1)
        swipes = get_number(datas['评论']) // 2  # 循环次数是总评论的一半
        max_no_find = 5  # 连续未找到符合条件用户的最大次数阈值
        no_find_count = 0
        i = 0
        last_user = set()
        while i < swipes:
            data = self.User_Talk_Page.get_data()
            if not data:
                print("没有更多数据，结束")
                break
            print(f"第 {i + 1} 次上滑，判断用户评论")
            found_in_this_swipe = False  # 本次上滑是否找到符合条件用户
            for item in data:
                print('评论区当前用户昵称', item['Nickname'])
                if item['Nickname'] in datas['Nickname']:
                    print("跳过视频主评论")
                    continue
                if item['Nickname'] in last_user:
                    continue
                last_user.add(item['Nickname'])
                if '已选中' in item['评论区赞']:
                    continue
                if self.um.find_user('Nickname', item['Nickname']):
                    print("用户已经处理过，跳过")
                    continue
                if not keyword_in_text(item['Content'], self.work['talk_key']):
                    print('没包含关键字')
                    continue
                if not keyword_in_text(item['时间'], self.config['user_talk_time']):
                    print('时间太久')
                    continue
                found_in_this_swipe = True
                # 继续判断其他用户，不跳出循环
                print("点击评论区用户头像")
                x, y = rect_center(item['评论区头像obj'].rect)
                if y < top or y > bottom:
                    continue
                print(x, y)
                action.click(x, y)
                time.sleep(2)
                datas = self.get_userData()
                self.back()
                if not datas or self.db.get_field(datas['DouyinID'], 'task'):
                    continue
                a = datas['Gender'] == self.work['Gender']
                b = datas['Age'] >= self.work['Age']
                c = datas['Region'] in self.work['Region']
                if not a and not b and not c:
                    self.db.update(datas['DouyinID'], {'task': 1})
                    print("性别/年龄/地区不符合")
                    continue

                elif not a and not b:
                    self.db.update(datas['DouyinID'], {'task': 1})
                    print("性别/年龄不符合")
                    continue
                elif not a and not c:
                    self.db.update(datas['DouyinID'], {'task': 1})
                    print("性别/地区不符合")
                    continue
                elif not b and not c:
                    self.db.update(datas['DouyinID'], {'task': 1})
                    print("年龄/地区不符合")
                    continue
                else:
                    item['评论区赞obj'].parent(1).click()

            if found_in_this_swipe:
                no_find_count = 0  # 重置连续未找到计数
            else:
                no_find_count += 1
                print(f"连续 {no_find_count} 次上滑未找到符合条件用户")

            if no_find_count >= max_no_find:
                print(f"连续 {max_no_find} 次未找到符合条件用户，退出循环")
                break

            swipe_up()  # 评论区上滑
            i += 1
            time.sleep(1)
        get_one(self.User_Talk_Page.关闭评论区ID).click()

    def 头像点赞(self):
        while not get_one(self.User_Home_Page.头像ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        print('头像点赞')
        t = get_one(self.User_Home_Page.头像ID)
        if '直播中' in t.desc:
            return
        t.click()
        time.sleep(2)
        get_one(self.User_Home_Page.头像赞ID).parent(1).click()
        time.sleep(2)
        get_one(self.User_Home_Page.头像赞关闭ID).click()
        time.sleep(1)

    def 短视频评论(self):
        print('短视频评论')
        while not get_one(self.User_Video_Page.评论ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        get_one(self.User_Video_Page.评论ID).parent(1).click()
        get_one(self.User_Talk_Page.评论区最大化ID)
        time.sleep(1)
        face_text = ''.join(random_text(self.face))
        # talk_text = random.choice(self.talk)  # say内容不添加
        p = get_one(self.User_Video_Page.评论框ID)
        p.click()
        time.sleep(1)
        text = str(face_text)
        get_one(self.User_Video_Page.评论框ID).input(text)
        x, y = rect_center(get_one(self.User_Video_Page.评论框发送TEXT, n=1).rect)
        action.click(x, y)
        get_one(self.User_Talk_Page.关闭评论区ID).click()

    def 分享短视频(self):
        print('分享短视频')
        while not get_one(self.User_Video_Page.分享ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        get_one(self.User_Video_Page.分享ID).click()
        time.sleep(1)
        get_one(self.User_Video_Page.推荐给朋友TEXT, n=1).parent(1).click()

    def 收藏短视频(self):
        print('收藏短视频')
        while not get_one(self.User_Video_Page.收藏ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        get_one(self.User_Video_Page.收藏ID).click()

    def 关注用户(self):
        print('关注用户')
        while not get_one(self.User_Video_Page.关注ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        get_one(self.User_Video_Page.关注ID).click()

    def 私信用户(self):
        print('私信用户')
        while not get_one(self.User_Home_Page.更多按钮ID):
            time.sleep(1)
        time.sleep(random.randint(7, 10))
        # self.GPT.check_money()
        get_one(self.User_Home_Page.更多按钮ID).click()
        time.sleep(1)
        get_one(self.User_Home_Page.发私信TEXT, n=1).parent(1).click()
        time.sleep(1)
        get_one(self.User_Msg_Page.私信页框架ID)
        time.sleep(1)
        msgbox = Selector().id(self.User_Msg_Page.私信页用户头像ID).find_all()
        texts = []
        for item in msgbox:
            brother_node = item.brother(2)
            if brother_node is None:
                continue  # 找不到兄弟节点，跳过
            text = brother_node.desc
            if not text:  # text是None或空字符串，跳过
                continue
            if text not in texts:
                texts.append(text)

        str = ",".join(texts)
        # str = self.GPT.ask(str)
        if random.random() > 0.5:
            text = random.choice(self.say)
            get_one(self.User_Msg_Page.私信页消息框ID).input(str)
            # get_one(self.User_Msg_Page.私信页消息框发送ID).click()
        else:
            d = Selector().id(self.User_Msg_Page.打招呼ID).find_all()
            if d:
                random.choice(d).click()
        time.sleep(1)
        self.back()

    def 其它(self):
        time.sleep(random.randint(7, 10))
        pass

    def handle_source(self):
        source_map = {
            '短视频': self.go_video,
            '直播间': self.go_live_room,
            '搜索': self.go_search,
            '用户粉丝列表': self.go_fans_list,
            '评论区': self.go_talk,
            '我的粉丝列表': self.go_my_fans_list,
        }
        if self.userSource in source_map:
            source_map[self.userSource]()
            return  # 调用完后立即返回，结束函数
        else:
            print(f"未知来源: {self.set_work}")

    def main(self):
        print("任务开始")
        self.set_work = "创业"  # 目标用户
        self.userSource = "用户粉丝列表"  # 任务来源
        self.work = self.config['work'][self.set_work]

        time.sleep(1)
        self.handle_source()

    def run(self):
        print("start")
        self.open_dy()
        self.jk_thread.start()
        self.tc_thread.start()
        while True:
            try:
                self.main()
                print("主程序启动")
            except Exception as e:
                import traceback
                print("主程序异常退出:", e)
                traceback.print_exc()
                raise


v = Work()

v.run()
